/*
 * Created by IntelliJ IDEA.
 * User: Lennart
 * Date: 17-dec-02
 * Time: 16:45:30
 */
package org.hkupp.gui.dialogs;

import be.proteomics.util.gui.JLabelAndTextPanel;
import be.proteomics.util.interfaces.Connectable;

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.SQLException;
import java.util.Properties;

/*
 * CVS information:
 *
 * $Revision: 1.5 $
 * $Date: 2007/03/15 14:17:49 $
 */

/**
 * This class implements a dialog to gather all information concerning a DB connection.
 *
 * @author Lennart Martens
 */
public class ConnectionDialog extends JDialog {

    private JTextField txtDriver = null;
    private JTextField txtUrl = null;
    private JTextField txtUser = null;
    private JPasswordField txtPassword = null;

    private JButton btnOK = null;
    private JButton btnCancel = null;

    private Connectable iTarget = null;

    private String iPropsFile = null;

    /**
     * This constructor takes as arguments the parent JFrame and
     * a title for the dialog.
     * It also constructs the GUI.
     *
     * @param   aParent JFrame that is the parent of this JDialog.
     * @param   aTarget Connectable to which the connection will be passed
     *                  once it is successfully established.
     * @param   aTitle  String with the title for this dialog
     * @param   aPropsFile  String with the filename for a propertiesfile with some connection
     *                      parameters already filled out, notably 'url' and 'driver'. Can be 'null'
     *                      for no defaults. If something goes wrong, no defaults are filled out.
     */
    public ConnectionDialog(JFrame aParent, Connectable aTarget, String aTitle, String aPropsFile) {
        super(aParent, aTitle, true);
        this.iPropsFile = aPropsFile;
        this.iTarget = aTarget;
        this.showConnectionDialog();
    }

    /**
     * This method actually shows the ConnectionDialog.
     * It takes care of the GUI related stuff.
     */
    private void showConnectionDialog() {
        this.addWindowListener(new WindowAdapter() {
            /**
             * Invoked when a window is in the process of being closed.
             * The close operation can be overridden at this point.
             */
            public void windowClosing(WindowEvent e) {
                cancelTriggered();
            }
        });
        this.constructScreen();
        if(getParent().getLocation().getX() <= 0 || getParent().getLocation().getY() <= 0) {
            this.setLocation(100, 100);
        } else {
            this.setLocation((int)getParent().getLocation().getX()+100, (int)getParent().getLocation().getY()+100);
        }
        this.pack();
        this.setResizable(false);
        this.tryToLoadParams();
    }

    /**
     * This method will initialize and lay-out all components.
     */
    private void constructScreen() {
        txtDriver = new JTextField(25);
        txtDriver.addKeyListener(new KeyAdapter() {
            /**
             * Invoked when a key has been typed.
             * This event occurs when a key press is followed by a key release.
             */
            public void keyTyped(KeyEvent e) {
                if(e.getKeyChar() == KeyEvent.VK_ENTER) {
                    txtUrl.requestFocus();
                } else {
                    super.keyTyped(e);
                }
            }
        });
        txtUrl = new JTextField(25);
        txtUrl.addKeyListener(new KeyAdapter() {
            /**
             * Invoked when a key has been typed.
             * This event occurs when a key press is followed by a key release.
             */
            public void keyTyped(KeyEvent e) {
                if(e.getKeyChar() == KeyEvent.VK_ENTER) {
                    txtUser.requestFocus();
                } else {
                    super.keyTyped(e);
                }
            }
        });
        txtUser = new JTextField(25);
        txtUser.addKeyListener(new KeyAdapter() {
            /**
             * Invoked when a key has been typed.
             * This event occurs when a key press is followed by a key release.
             */
            public void keyTyped(KeyEvent e) {
                if(e.getKeyChar() == KeyEvent.VK_ENTER) {
                    txtPassword.requestFocus();
                } else {
                    super.keyTyped(e);
                }
            }
        });
        txtPassword = new JPasswordField(25);
        txtPassword.addKeyListener(new KeyAdapter() {
            /**
             * Invoked when a key has been typed.
             * This event occurs when a key press is followed by a key release.
             */
            public void keyTyped(KeyEvent e) {
                if(e.getKeyChar() == KeyEvent.VK_ENTER) {
                    connectTriggered();
                } else {
                    super.keyTyped(e);
                }
            }
        });

        JLabelAndTextPanel jpanTop = new JLabelAndTextPanel(new JLabel[] {new JLabel("Database driver"), new JLabel("Database URL"), new JLabel("Username"), new JLabel("Password")},
                                                            new JTextField[]{txtDriver, txtUrl, txtUser, txtPassword});
        jpanTop.setBorder(BorderFactory.createTitledBorder("Connection settings"));

        btnOK = new JButton("Connect");
        btnOK.setMnemonic(KeyEvent.VK_O);
        btnOK.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                connectTriggered();
            }
        });
        btnOK.addKeyListener(new KeyAdapter() {
            /**
             * Invoked when a key has been typed.
             * This event occurs when a key press is followed by a key release.
             */
            public void keyTyped(KeyEvent e) {
                if(e.getKeyChar() == KeyEvent.VK_ENTER) {
                    connectTriggered();
                }
            }
        });
        btnCancel = new JButton("Cancel");
        btnCancel.setMnemonic(KeyEvent.VK_C);
        btnCancel.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                cancelTriggered();
            }
        });
        btnCancel.addKeyListener(new KeyAdapter() {
            /**
             * Invoked when a key has been typed.
             * This event occurs when a key press is followed by a key release.
             */
            public void keyTyped(KeyEvent e) {
                if(e.getKeyChar() == KeyEvent.VK_ENTER) {
                    cancelTriggered();
                }
            }
        });

        JPanel jpanButtons = new JPanel();
        jpanButtons.setLayout(new BoxLayout(jpanButtons, BoxLayout.X_AXIS));

        jpanButtons.add(Box.createHorizontalGlue());
        jpanButtons.add(btnOK);
        jpanButtons.add(Box.createRigidArea(new Dimension(15, btnOK.getHeight())));
        jpanButtons.add(btnCancel);
        jpanButtons.add(Box.createRigidArea(new Dimension(10, btnOK.getHeight())));

        JPanel jpanTotal = new JPanel();
        jpanTotal.setLayout(new BoxLayout(jpanTotal, BoxLayout.Y_AXIS));
        jpanTotal.add(jpanTop);
        jpanTotal.add(Box.createRigidArea(new Dimension(jpanTop.getWidth(), 10)));
        jpanTotal.add(jpanButtons);

        this.getContentPane().add(jpanTotal, BorderLayout.CENTER);
    }

    /**
     * This method is called when the user attempts to connect.
     */
    private void connectTriggered() {
        String driverClass = txtDriver.getText().trim();
        String url = txtUrl.getText().trim();
        String user = txtUser.getText().trim();
        String password = new String(txtPassword.getPassword()).trim();

        if(driverClass.equals("")) {
            JOptionPane.showMessageDialog(this, "Driver class needs to be specified!", "No driver specified!", JOptionPane.ERROR_MESSAGE);
            txtDriver.requestFocus();
            return;
        }

        if(url.equals("")) {
            JOptionPane.showMessageDialog(this, "Database URL needs to be specified!", "No URL specified!", JOptionPane.ERROR_MESSAGE);
            txtUrl.requestFocus();
            return;
        }

        String errorString = null;
        Connection lConn = null;
        try {
            this.setCursor(new Cursor(Cursor.WAIT_CURSOR));
            Driver d = (Driver)Class.forName(driverClass).newInstance();
            Properties lProps = new Properties();
            if(user != null) {
                lProps.put("user", user);
            }
            if(password != null) {
                lProps.put("password", password);
            }
            lConn = d.connect(url, lProps);
            if(lConn == null) {
                errorString = "Could not connect to the database. Either your driver is incorrect for this database, or your URL is malformed.";
            }
        } catch(ClassNotFoundException cnfe) {
            errorString = "Driver class was not found! (" + cnfe.getMessage() + ")";
        } catch(IllegalAccessException iae) {
            errorString = "Could not access default constructor on driver class! (" + iae.getMessage() + ")";
        } catch(InstantiationException ie) {
            errorString = "Could not create instance of driver class! (" + ie.getMessage() + ")";
        } catch(SQLException sqle) {
            errorString = "Database refused connection! (" + sqle.getMessage() + ")";
        }
        this.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
        if(errorString != null) {
            JOptionPane.showMessageDialog(this, new String[]{"Unable to make the connection to '" + url + "' using '" + driverClass + "'!", errorString, "\n"}, "Unable to connect!", JOptionPane.ERROR_MESSAGE);
            return;
        } else {
            JOptionPane.showMessageDialog(this, new String[]{"Connection to '" + url + "' established!", "\n"}, "Connection established!", JOptionPane.INFORMATION_MESSAGE);
            iTarget.passConnection(lConn, url.substring(url.lastIndexOf(":")+1));
            this.setVisible(false);
            this.dispose();
        }
    }

    /**
     * This method is called when the user presses cancel.
     */
    private void cancelTriggered() {
        this.setVisible(false);
        this.dispose();
        iTarget.passConnection(null, "");
    }

    /**
     * This method attempts to load connection parameters from
     * a properties file in the classpath.
     * If this file is not found, nothing happens.
     * If it is found, the parameters found will be filled out.
     */
    private void tryToLoadParams() {
        if(iPropsFile != null) {
            try {
                Properties p = new Properties();
                InputStream is = ClassLoader.getSystemResourceAsStream(iPropsFile);
                if(is == null) {
                    is = this.getClass().getClassLoader().getResourceAsStream(iPropsFile);
                    if(is == null) {
                        // Leave it at that.
                        return;
                    }
                    System.out.println("local classloader.");
                }
                p.load(is);
                String dbDriver = p.getProperty("driver");
                if(dbDriver == null) {
                    dbDriver = p.getProperty("DRIVER");
                }
                String dbUrl = p.getProperty("url");
                if(dbUrl == null) {
                    dbUrl = p.getProperty("URL");
                }
                if(dbDriver != null) {
                    this.txtDriver.setText(dbDriver.trim());
                }
                if(dbUrl != null) {
                    this.txtUrl.setText(dbUrl.trim());
                }
                is.close();
            } catch(Exception e) {
                // Do nothing.
                e.printStackTrace();
            }
        }
    }
}
